﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ReadyApps.Data.Wiki
{
    // TODO: Unit test PermissionSet
    /// <summary>
    /// Represents a set of permissions that may be used on an object within
    /// the wiki system.
    /// </summary>
    /// <seealso cref="RolePrivileges"/>
    [Serializable]
    public class PermissionSet
    {
        #region Constants and Variables

        private const int IMPOSSIBLE_ID = 0;

        private List<RolePrivileges> rolePrivileges;

        #endregion

        #region Properties

        /// <summary>
        /// Gets the unique identifier of the permission set.
        /// </summary>
        public int ID { get; private set; }

        /// <summary>
        /// Gets an <see cref="IEnumerable{RolePrivileges}"/> of the roles and
        /// their associated privileges.
        /// </summary>
        public IEnumerable<RolePrivileges> RolePrivileges
        {
            get { return rolePrivileges.AsReadOnly(); }
        }

        /// <summary>
        /// Gets the privileges associated with a named role.
        /// </summary>
        /// <param name="roleName">The name of the role to get privileges
        /// for.</param>
        /// <returns>A <see cref="RolePrivileges"/> object for the specified
        /// role.</returns>
        /// <exception cref="ArgumentOutOfRangeException">The specified role
        /// name does not exist in the collection.</exception>
        public RolePrivileges this[string roleName]
        {
            get
            {
                RolePrivileges privs = rolePrivileges.FirstOrDefault(p => p.RoleName == roleName);
                if (privs == default(RolePrivileges))
                {
                    throw new ArgumentOutOfRangeException("roleName");
                }
                return privs;
            }
        }

        /// <summary>
        /// Gets a value indicating whether or not the permissions already exist
        /// or if they are new.
        /// </summary>
        public bool IsExistingSet
        {
            get { return ID != IMPOSSIBLE_ID; }
        }

        #endregion

        #region Constructors

        /// <summary>
        /// Creates a new <see cref="PermissionSet"/> object for a new permission
        /// set.
        /// </summary>
        public PermissionSet()
            : this(IMPOSSIBLE_ID) { }

        /// <summary>
        /// Creates a new <see cref="PermissionSet"/> object for existing permissions
        /// with the specified unique identifier.
        /// </summary>
        /// <param name="id"></param>
        public PermissionSet(int id)
        {
            ID = id;
            rolePrivileges = new List<RolePrivileges>();
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Adds the given role name to the collection of included roles and
        /// returns a new <see cref="RolePrivileges"/> object for the role.
        /// </summary>
        /// <param name="role">The role name to add to the collection.</param>
        /// <returns>A <see cref="RolePrivileges"/> object from the collection
        /// for the specified role.</returns>
        /// <exception cref="ArgumentException">The role has already been added
        /// to the collection.</exception>
        public RolePrivileges AddRole(string role)
        {
            if (rolePrivileges.Where(r => r.RoleName == role).Count() > 0)
            {
                throw new ArgumentException(string.Format("'{0}' role already exists in the collection.", role), "role");
            }
            RolePrivileges newPrivileges = new RolePrivileges(role);
            rolePrivileges.Add(newPrivileges);
            return newPrivileges;
        }

        /// <summary>
        /// Removes the given role from the collection of included roles.
        /// </summary>
        /// <param name="role">The role name to remove from the collection.</param>
        /// <exception cref="ArgumentException">The role does not exist in the
        /// collection of roles.</exception>
        public void RemoveRole(string role)
        {
            RolePrivileges privs = rolePrivileges.FirstOrDefault(r => r.RoleName == role);
            if (privs == null)
            {
                throw new ArgumentException(string.Format("'{0}' role does not exist in the collection.", role), "role");
            }
            rolePrivileges.Remove(privs);
        }

        #endregion
    }
}
